//
//  UwantSocketClient.swift
//  uwantgoeasy
//
//  Created by 冯传宝 on 12/9/15.
//  Copyright © 2015 uwantsoft. All rights reserved.
//

import Foundation
import CocoaAsyncSocket
class UwantSocketClient: NSObject, GCDAsyncSocketDelegate{
    var addr:String?
    var port :UInt16?
    let timeout:Double = 5.0
    var socket: GCDAsyncSocket!
    var status:ClientStatus = ClientStatus.INITIAL
    
    var hearBeatPinger:HeartBeatPinger?
    var messageSender:MessageSender?
    var socketClientListener: SocketClientListener?
    
    
    class func shareInstance(server: String,port: UInt16)->UwantSocketClient{
        struct UWSingleton{
            static var predicate:dispatch_once_t = 0
            static var instance:UwantSocketClient? = nil
        }
        dispatch_once(&UWSingleton.predicate,{
            UWSingleton.instance=UwantSocketClient(server: server, port: port)
            }
        )
        return UWSingleton.instance!
    }
    
    private init(server: String,port: UInt16){
        super.init()
        self.addr = server
        self.port = port
        hearBeatPinger = HeartBeatPinger(socketClient: self)
        messageSender = MessageSender(socketClient: self)
        self.setupConnection()
    }
    
    func setupConnection(){
        do {
            print("try to create socket...")
            socket = GCDAsyncSocket(delegate: self, delegateQueue: dispatch_get_main_queue())
            try socket.connectToHost(addr, onPort: port!, withTimeout: timeout)
        } catch let e {
            print("try connect failed:\(e)")
            socketClientListener?.onConnectFailed(UwantSocketError.SERVER_UNREACHABLE)
            sleep(1)
            setupConnection()
        }
    }
    
    func socket(socket : GCDAsyncSocket, didConnectToHost host:String, port p:UInt16) {
        print("Connected to \(host) on port \(p).")
        status = ClientStatus.CONNECTED
        socketClientListener?.onConnect()
        hearBeatPinger!.start()
        socket.readDataWithTimeout(-1, tag: 0)
    }
    
    func socket(socket : GCDAsyncSocket!, didReadData data:NSData!, withTag tag:Int){
        
        let content = String(bytes: readDataToByteArray(data), encoding: NSUTF8StringEncoding)
        if let data = content!.dataUsingEncoding(NSUTF8StringEncoding){
            
            do {
                let json = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers) as! NSMutableDictionary
                let type = json["type"] as! String
                if(UwantServerMessageType.PONG.rawValue == type){
                    if let pongContent = json["content"] as? [Int]{
                        var uint8Content:[UInt8] = Array(count: pongContent.count, repeatedValue: 0)
                        for (index, value) in pongContent.enumerate(){
                            uint8Content[index] = UInt8(value)
                        }
                        let pingStartStr = String(bytes: uint8Content, encoding: NSUTF8StringEncoding)
                        let lastPingSeconds:Double = NSString(string: pingStartStr!).doubleValue
                        hearBeatPinger!.onPong(lastPingSeconds)
                    }
                }else if(UwantServerMessageType.MESSAGE.rawValue == type){
                    if let pongContent = json["content"] as? [Int]{
                        var uint8Content:[UInt8] = Array(count: pongContent.count, repeatedValue: 0)
                        for (index, value) in pongContent.enumerate(){
                            uint8Content[index] = UInt8(value)
                        }
                        socketClientListener?.onMessage(uint8Content)
                    }
                }

            }catch{
                print("error")
            }
        }
        socket.readDataWithTimeout(-1, tag: 0)
    }
    private func readDataToByteArray(data:NSData!)->[UInt8]{
        var lengthArrReverse = [UInt8](count: 4, repeatedValue: 0)
        data.getBytes(&lengthArrReverse, length: 4)
        let lengthArr = Array(lengthArrReverse.reverse())
        let length:Int32 = ByteArrayUtils.fromByteArray(lengthArr, Int32.self)
        var contentByteArray = [UInt8](count: Int(length), repeatedValue: 0)
        data.getBytes(&contentByteArray, range: NSMakeRange(4, Int(length)))
        return contentByteArray
    }
    
    func socketDidDisconnect(sock: GCDAsyncSocket!, withError err: NSError!) {
        print("socket disconnected with err \(err)")
        hearBeatPinger?.stop()
        disconnect(UwantSocketError.AUTO_DISCONNECT)
    }
    
    func disconnect(error:UwantSocketError){
        if(status != ClientStatus.DISCONNECTED){
            socket.disconnect()
            status = ClientStatus.DISCONNECTED
            socketClientListener?.onDisconnect(error)
        }
        sleep(1)
        self.setupConnection()
    }
    
    var _socketClientListener:SocketClientListener{
        get{
            return self.socketClientListener!
        }
        set(socketClientListener){
            self.socketClientListener = socketClientListener
        }
    }
}